/*
 * Projet Implementation Reseau
 * Groupe:
 *    - Bertrand PROUHET
 *     - Arthur HALET
 *    - Renaud HUMBLOT
 *    - Vlad MARIN
 */

#include "fonctions.h"


/************************************************************/
// int** type_socket = malloc((MAX_CONNEXIONS * MAX_CONNEXIONS) * sizeof(int));
// int compteur_connexions = 0;
/************************************************************/

/*
 * Variables Globales
 */
int indice_Lecture = 0;
int g_numero_sequence;
int g_numero_ack;

/*
 * Methodes supplementaires.
 */



// Cree et alloue de l'espace pour un SocketTCP.
struct SocketTCP *creatSocketTCP() {
	// Declaration et allocation memoire.
	struct SocketTCP *new_socket = malloc(sizeof(struct SocketTCP));
	if (new_socket == NULL) {
		perror("ERROR: Allocation memoire socket de communication.");
		return NULL;
	}
	new_socket->new_addr = malloc(sizeof(struct sockaddr_in));
	new_socket->new_addr_len = sizeof(struct sockaddr_in);

	// Creation.
	if((new_socket->descripteur = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("ERROR: Creation socket de communication.");
		return NULL;
	}
	printf("INFO: SOCKET COMMUNICATION = %d\n", new_socket->descripteur);
	return new_socket;
}



// Initialiser un tcp_packet.
void initialiserTCPPacket(struct tcp_packet* packet) {
	memset(&packet, 0, sizeof(packet));
}

struct SocketTCP socketTcps[MAX_CONNEXIONS];
int ptrSocketTcps=0;



/*
 * sockfd     = socket d'ecoute du serveur
 * addr     = structure adresse client
 * addrlen     = taille adresse client
 *
 * RETURN   = descripteur nouveau socket de communication
 */
int f_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
	// Declarations.
	printf("////////////////// \n");
	struct SocketTCP* new_socket;
	struct sockaddr_in* addr_communication;
	struct tcp_packet received_packet;
	struct tcp_packet sent_packet;
	unsigned long numero_seq_client_avenir = 0;
	unsigned long numero_ack_client_avenir = 0;
	int tab_clients[MAX_CONNEXIONS];
	unsigned long tab_client_seq_avenir[MAX_CONNEXIONS];
	unsigned long tab_client_ack_avenir[MAX_CONNEXIONS];
	int nb_demandes = 0;
	int nb_realisees = 0;
	printf("============= 0\n");
	// Initialisation tableaux.
	int i;
	for(i = 0; i < MAX_CONNEXIONS; i++) {
		tab_client_seq_avenir[i] = 0;
		tab_client_ack_avenir[i] = 0;
	}
	printf("============= 1\n\n");
	// Initialisation packets.
	initialiserTCPPacket(&received_packet);
	printf("============= 2\n");
	// Creation de la nouvelle socket de communication.
	new_socket = creatSocketTCP();
	if (new_socket == NULL) {
		perror("ERROR: Socket non cree.");
		return 1;
	}
	printf("INFO: SOCKET COMMUNICATION = %d\n", new_socket->descripteur);

	// Attente d'un packet SYN.
	while(recvfrom(sockfd, &received_packet, sizeof (struct tcp_packet), 0, addr, addrlen) != -1) {
		printf("jojo\n");
		// Reception d'un packet SYN.
		if (compareFlag("1000", &received_packet) == 0) {
			printf("jojo ttt\n");
			// Si il reste de la place pour les connexions et que le client n'est pas deja en traitement.
			if ((nb_demandes < MAX_CONNEXIONS) && !(estClientExistant(received_packet.ID_emmeteur, tab_clients))) {
				
				// Initialisation tcp_packet.
				initialiserTCPPacket(&sent_packet);
				sent_packet.ID_emmeteur 	= getpid();
				// sent_packet.syn 			= SYN;
				// sent_packet.ack 			= ACK;
				setFlag("1100", &sent_packet);
				sent_packet.numero_sequance = received_packet.numero_ack;
				sent_packet.numero_ack 		= received_packet.numero_sequance + 1;
				printf("============= 3\n");
				// Envoi d'un SYN+ACK.
				if (sendto(sockfd, &sent_packet, sizeof (struct tcp_packet), 0, addr, *addrlen) == -1) {
					perror("ERROR: Renvoi de SYN+ACK");
					return 1;
				}
				printf("============= 4\n");
				// Calcul du prochain SEQ et ACK a recevoir.
				numero_seq_client_avenir = sent_packet.numero_ack;
				numero_ack_client_avenir = sent_packet.numero_sequance + 1;

				// Enregistrer la demande.
				nb_demandes++;
				tab_clients[nb_demandes - 1] = received_packet.ID_emmeteur;
				tab_client_seq_avenir[nb_demandes - 1] = numero_seq_client_avenir;
				tab_client_ack_avenir[nb_demandes - 1] = numero_ack_client_avenir;
				printf("*****************\n");
			}
		}

		// Reception d'un packet ACK.
		if (compareFlag("0100", &received_packet) == 0) {
			// Si on a deja recu un SYN de la part du client.
			if (estClientExistant(received_packet.ID_emmeteur, tab_clients)) {
				// On verifie la coherence des numeros de SEQ et ACK.
				if (received_packet.numero_sequance == tab_client_seq_avenir[getIndiceClientExistant(received_packet.ID_emmeteur, tab_clients)]) {
					
					// Effacement de la commande du client - elle va etre traitee.
					tab_client_seq_avenir[getIndiceClientExistant(received_packet.ID_emmeteur, tab_clients)] = 0;
					tab_client_ack_avenir[getIndiceClientExistant(received_packet.ID_emmeteur, tab_clients)] = 0;
					tab_clients[getIndiceClientExistant(received_packet.ID_emmeteur, tab_clients)] = 0;
					nb_realisees++;
					printf("============= 5\n");
					// On fait un bind entre la nouvelle socket et le client.
					addr_communication = malloc(sizeof(struct sockaddr_in));
					addr_communication->sin_family 		= AF_INET;
					addr_communication->sin_addr.s_addr  = htonl(INADDR_ANY);
					addr_communication->sin_port 		=  (SERVER_PORT + 10);
					if(bind(new_socket->descripteur, (struct sockaddr*) addr_communication, sizeof(struct sockaddr_in)) == -1) { 
						perror("ERROR: F_ACCEPT - problem with bind");
						return 1;
					}
					printf("=============6 \n");
					// ACK recu et numero SEQ correct - envoi descripteur nouveau socket de communication.
					new_socket->new_addr = addr_communication;
					new_socket->new_addr_len = sizeof(struct sockaddr_in); 
					socketTcps[ptrSocketTcps] = *new_socket;
					printf("PORT ACCEPT: %d\n", new_socket->new_addr->sin_port);
    				ptrSocketTcps++;
					return new_socket->descripteur;
				}
			}
		}
	}
	return 1;
}



int     f_connect(int sockfd, struct sockaddr *addr, socklen_t addrlen ) {
	char * bla = inet_ntoa(((struct sockaddr_in*)addr)->sin_addr);
	printf("%d %s \n", htons(((struct sockaddr_in*)addr)->sin_port), bla);
    // Declarations.
    int isn;
    struct tcp_packet *received_packet = malloc (sizeof (struct tcp_packet));
    struct tcp_packet sent_packet;
    struct SocketTCP socketTcp;
    fd_set readfs;
    int retry;

    FD_ZERO(&readfs);
    FD_SET(sockfd, &readfs);
    printf("dans le connect 1\n");

    sent_packet.ID_emmeteur = getpid();
    sent_packet.port_source = 0;
    sent_packet.port_destination = ((struct sockaddr_in*) addr)->sin_port;
    isn = setISNTcpPacket(&sent_packet);
    setFlag("1000", &sent_packet);
    retry = NBRETRY;
    while(retry >0){
        if (sendto(sockfd, &sent_packet, sizeof (struct tcp_packet), 0, addr, addrlen) == -1) {
            perror("ERROR: Envoi de SYN.");
            return -1;
        }
        if(timeoutSocketTcp(sockfd, &readfs, NULL) > 0){
            recvfrom(sockfd, received_packet, sizeof(struct tcp_packet), 0, addr, &addrlen);
            break;
        } else {
            retry--;
        }
    }
    if(retry == 0){
    	perror("timeout");
        return -1;
    }
    if(compareFlag("1100", received_packet)!=0){
        perror("syn+ack not received");
        return -1;
    }
    if((int)received_packet->numero_ack != isn+1){
    	perror("Sequence number unvalid");
    	return -1;
    }
    setFlag("0100", &sent_packet);
    sent_packet.numero_ack = received_packet->numero_sequance++;
    sent_packet.numero_sequance++;
    retry = NBRETRY;
    if (sendto(sockfd, &sent_packet, sizeof (struct tcp_packet), 0, addr, addrlen) == -1) {
        perror("ERROR: Renvoi de ACK.");
        return -1;
    }
    socketTcp.descripteur = sockfd;
    socketTcp.new_addr_len = addrlen;
    socketTcp.new_addr = ((struct sockaddr_in*) addr);
    socketTcps[ptrSocketTcps] = socketTcp;
    ptrSocketTcps++; 
    // return 0;
    return received_packet->port_destination;
}			

		
int 	f_listen(int sockfd, int backlog) {
	return 0;
}



int f_close(struct SocketTCP* socket) {
	shutdown(socket->descripteur, SHUT_RDWR);
	free(socket);
	return 0;
}

/*
 *	fd 				- 	Descripteur du socket
 *	buf 			-	Buffer à remplir
 *	count 			-	Nombre d'Octets à Lire
 *	RETURN VLAUE	- 	-1 S'il y a erreur dans un recvfrom  | sendto
 *					-	2 Si le packet reçu n'est celui escompté
 */

ssize_t f_read(int fd, void *buf, size_t count){
	int n;
    struct sockaddr_in* serv_addr;
    socklen_t addr_len;
    struct SocketTCP* socketTcp; 
    addr_len = sizeof (serv_addr);
    char* buf_intermediaire = NULL;
    struct tcp_packet* buf_data;
    struct tcp_packet* ptr = buf;
    struct tcp_packet* packet = (struct tcp_packet*)buf;
    struct tcp_packet *received_packet = malloc (sizeof (struct tcp_packet));
    socketTcp = getSocketTcpFromFd(fd);
    printf("%d\n", socketTcp->new_addr->sin_port);
    serv_addr = socketTcp->new_addr;
    addr_len = socketTcp->new_addr_len;
    // TODO : Verifier l'adresse du serveur
    printf("&&&&&&&&&&&&&&&&&&&&&&&&&&\n");
    //n = recvfrom(fd, buf_intermediaire, sizeof (struct tcp_packet), 0,(struct sockaddr *) serv_addr, &addr_len);
    fd_set readfs;
    FD_ZERO(&readfs);
    FD_SET(fd, &readfs);
    n = timeoutSocketTcp(fd, &readfs, NULL);
    if((n = timeoutSocketTcp(fd, &readfs, NULL)) > 0){
            recvfrom(fd, received_packet, sizeof(struct tcp_packet), 0, (struct sockaddr *) serv_addr, &addr_len);
        } else {
            return -1;
        }
    printf("apres recevfrom dans read\n");
    
    
    // Parcours des données récues
    for (buf_data = (struct tcp_packet*)buf_intermediaire; n > 0; n--){
        ptr[0] = buf_data[0];
        ptr++;
        buf_data++;          
    }
    // Contrôle des numeros de sequence et d'acquittement
    // Si on reçoit un packet FIN
    if (compareFlag("0010", packet) == 0){
        
        // Mettre le flag RST à 1 
        if(setFlag("0001", packet) != 0)
        {
            return -1;
        }
        if (sendto(fd, (struct tcp_packet*)buf, sizeof (struct tcp_packet), 0, (struct sockaddr *) serv_addr, sizeof (serv_addr)) == -1){
            perror("sendto");
            return -1;
        }
        free(buf);   
        return 1;
    } else if(compareFlag("1000",packet)){
        if (indice_Lecture == 0){
            
            g_numero_sequence = packet->numero_ack ;
            g_numero_ack = packet->numero_sequance;

            packet->numero_sequance = g_numero_ack;
            packet->numero_ack = g_numero_sequence + n;                
            if (sendto(fd, (struct tcp_packet*)buf, sizeof (struct tcp_packet), 0, (struct sockaddr *) serv_addr, sizeof (serv_addr)) == -1){
                perror("sendto");
                return -1;
            }
        } else {
            if ((packet->numero_sequance) == g_numero_ack){
                
                packet->numero_sequance = g_numero_ack;
                packet->numero_ack = g_numero_sequence + n;
                
                g_numero_sequence = packet->numero_sequance;
                g_numero_ack = packet->numero_ack ;

                if (sendto(fd, (struct tcp_packet*)buf, sizeof (struct tcp_packet), 0, (struct sockaddr *) serv_addr, sizeof (serv_addr)) == -1){
                    perror("sendto");
                    return -1;
                }
            } else { // Demande de renvoi du packet attendu
                return -2 ; 
            }
        }
    } else {printf("ERROR : Type de packet non attendu\n"); return 0 ; }    

    indice_Lecture++;
    return n;
}


ssize_t f_write(int fd, const void *buf, size_t count) {
	// Declarations.
	// struct sockaddr_in adresse_serveur;
	// struct tcp_packet* received_packet;
	// struct tcp_packet sent_packet;

	// // Initialisations.
	// adresse_serveur = (getSocketTcpFromFd(fd))->new_addr; 
	// initialiserTCPPacket(&sent_packet);
	// received_packet = malloc (sizeof (struct tcp_packet));

	// sent_packet->ID_emmeteur = getpid();
	//sent_packet->port_source = 
	//sent_packet->port_destination = 
	// regarder dans la strucutre connexion/socketTCP pour chopper les derniers numeros de SEQ et ACK.

	return 0;
}

int setISNTcpPacket(struct tcp_packet *sent_packet){
	int isn;
	srand(time(NULL));
	isn = rand()%6046+1;
	sent_packet->numero_sequance = isn;
	return isn;
}

/*
 * Methodes supplementaires.
 */

int timeoutSocketTcp(int sock, fd_set *readfd, fd_set *writefd){
    int readsocks;
    struct timeval timeout;
    
    timeout.tv_sec = TIMEOUT;
    timeout.tv_usec = 0;
    readsocks = select(sock+1, readfd, writefd, NULL, &timeout);
    if (readsocks < 0) {
        perror("select");
        return 1;
    }
    return readsocks;
}

/*
    put flag inside tcp_packet format 1 and 0 (syn,ack,fin,rst) 0010
 */
int compareFlag(char * flag, struct tcp_packet *sent_packet){
    char flagPacket[5];
    flagPacket[0] = sent_packet->syn;
    flagPacket[1] = sent_packet->ack;
    flagPacket[2] = sent_packet->fin;
    flagPacket[3] = sent_packet->rst;
    flagPacket[4] = '\0';
    return strcmp(flag, flagPacket); 
}
int setFlag(char *flag, struct tcp_packet *sent_packet) {
    if(strlen(flag) < 4){
        perror("Flag too short");
        return 1;
    }
    sent_packet->syn = flag[0];
    sent_packet->ack = flag[1];
    sent_packet->fin = flag[2];
    sent_packet->rst = flag[3];
    return 0;
}

int estClientExistant(int ID, int* tab_ID) {
	int i;
	for (i = 0; i < MAX_CONNEXIONS; i++) {
		if (tab_ID[i] == ID) {
			return 1;
		}
	}
	return 0;
}


int getIndiceClientExistant(int ID, int* tab_ID) {
	int i;
	for (i = 0; i < MAX_CONNEXIONS; i++) {
		if (tab_ID[i] == ID) {
			return i;
		}
	}	
	return -1;
}

struct SocketTCP * getSocketTcpFromFd(int fd){
	int i;
	for (i = 0; i < MAX_CONNEXIONS; i++){
		if(socketTcps[i].descripteur == fd){
			return &socketTcps[i];
		}
	}
	return NULL;
}
